using System;
using UnityEngine;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;

using System.Text.RegularExpressions;
using UnityEditor.Animations;
/// Create by xhy
/// Time:2024_1_12
/// <summary>
/// 根据sprites图集动画名，自动生成各种序列帧动画片段
/// </summary>

public partial class Fruit2DAnimTools
{

    public class Frame2AnimData
    {
        public Texture atlas;//图集
        public string animControlPath;//状态机生成路径
        public string clipPath;//动画片段生成路径
        public string prefabPath;//预制体生成路径
        public bool isLoop;//是否循环
        public string resPath;//资源名称
        public string framePrefix;//序列帧前缀
        public int frameRate;//帧率
        public int frameNum;//每秒播多少次
        public bool needBornPrefab;//是否新生成预制体
        public Frame2AnimData(Texture atlas, string animControlPath, string clipPath, string prefabPath, string resPath, string framePrefix, int frameRate, bool isLoop, bool needBornPrefab)
        {
            this.atlas = atlas;
            this.animControlPath = animControlPath;
            this.clipPath = clipPath;
            this.prefabPath = prefabPath;
            this.resPath = resPath;
            this.framePrefix = framePrefix;
            this.isLoop = isLoop;
            this.frameRate = frameRate;
            this.needBornPrefab = needBornPrefab;
        }
    }

    /// <summary>
    /// 返回此图集下需要创建的动画信息
    /// </summary>
    /// <param name="f2d"></param>
    /// <returns></returns>
    public static Dictionary<string, List<string>> GetAnimationInfo(Texture atlas)
    {
        List<string> animaInfo = new List<string>();
        string path = AssetDatabase.GetAssetPath(atlas);
        //先提取指定ID前缀的动画
        Sprite[] sprites = AssetDatabase.LoadAllAssetRepresentationsAtPath(path)
            .Select(x => x as Sprite).ToArray();

        Regex regex = new Regex(@"^(\d*)_(\w*)_", RegexOptions.IgnoreCase);

        foreach (var sprite in sprites)
        {
            var match = regex.Match(sprite.name);
            var prefixName = match.Value;
            prefixName = prefixName.Substring(0, prefixName.Length - 1).ToLowerInvariant(); //删去最后的_符号

            if (!animaInfo.Contains(prefixName))
            {
                animaInfo.Add(prefixName);
            }

        }

        Dictionary<string, List<string>> clips = new Dictionary<string, List<string>>();
        foreach (var t in animaInfo)
        {
            var s = t.Split('_');
            var animId = s[0];
            var animName = s[1].ToLower();//40011_atk1 => atk1
            animName = System.Threading.Thread.CurrentThread.CurrentCulture.TextInfo.ToTitleCase(animName);//首字母大写，和Anamitor空动画名匹配

            if (clips.ContainsKey(animId))
            {
                clips[animId].Add(animName);
            }
            else
            {
                clips.Add(animId, new List<string> { animName });
            }
        }



        return clips;
    }

    /// <summary>
    /// 创建动画帧
    /// </summary>
    /// <param name="f2d"></param>
    /// <param name="curveBindingType"></param>
    /// <param name="isMirrorAniClip"></param>
    public static void BuildAllAnimation(Frame2AnimData f2d, System.Type curveBindingType, bool isMirrorAniClip = true)
    {

        string path = AssetDatabase.GetAssetPath(f2d.atlas);
        //先提取指定ID前缀的动画
        Sprite[] sprites = AssetDatabase.LoadAllAssetRepresentationsAtPath(path)
            .Select(x => x as Sprite).Where(x => x.name.Contains(f2d.framePrefix)).ToArray();

        Regex regex = new Regex(@"^(\d*)_(\w*)_", RegexOptions.IgnoreCase);

        Dictionary<string, List<Sprite>> tmpSpriteMap = new Dictionary<string, List<Sprite>>();
        foreach (var sprite in sprites)
        {
            var match = regex.Match(sprite.name);
            var prefixName = match.Value;
            prefixName = prefixName.Substring(0, prefixName.Length - 1).ToLowerInvariant(); //删去最后的_符号
        
            if (tmpSpriteMap.ContainsKey(prefixName))
            {
                tmpSpriteMap[prefixName].Add(sprite);
            }
            else
            {
                tmpSpriteMap.Add(prefixName,new List<Sprite> { sprite });
            }
        }
        //排序
        foreach(var t in tmpSpriteMap)
        {
            t.Value.Sort(delegate (Sprite item1, Sprite item2)
            {
                return item1.name.CompareTo(item2.name);
            });
        }

        //1.先创建所有序列帧动画
        Dictionary<string, AnimationClip> clips = new Dictionary<string, AnimationClip>();
        foreach (var t in tmpSpriteMap)
        {
            //映射好动画名对应的clip
            var animName = t.Key.Split('_')[1].ToLower();//40011_atk1 => atk1
            animName = System.Threading.Thread.CurrentThread.CurrentCulture.TextInfo.ToTitleCase(animName);//首字母大写，和Anamitor空动画名匹配

            f2d.framePrefix = animName;//Atk1
            f2d.isLoop = animName.Equals("Run");//移动是循环动画
            var c = BuildAnimation(t.Value.ToArray(),f2d, curveBindingType, isMirrorAniClip);

            clips.Add(animName.ToLower(), c);            
        }
 
        //2.创建 AnimatorOverrideController
        string animatorPath = string.Format("Assets/{0}/{1}", f2d.animControlPath, f2d.resPath);
        if (!Directory.Exists(animatorPath))
        {
            Directory.CreateDirectory(animatorPath);
        }
        string animtorName = string.Format("{0}_Override", f2d.resPath);
        var controller = BuildAnimationOverrideController(clips, f2d.animControlPath, animatorPath, animtorName);

        if (f2d.needBornPrefab)
        {
            string prefabPath = string.Format("Assets/{0}/{1}", f2d.prefabPath, f2d.resPath);
            if (!Directory.Exists(prefabPath))
            {
                Directory.CreateDirectory(prefabPath);
            }
            string prefabName = string.Format("{0}_Show", f2d.resPath);
            BuildPrefab(prefabPath, prefabName, controller, sprites[0], curveBindingType);
        }
    }

    private static AnimationClip BuildAnimation(Sprite[] sprites,Frame2AnimData f2d, System.Type curveBindingType, bool isMirrorAniClip = true)
    {
        string animationName = f2d.framePrefix;

        if (isMirrorAniClip) sprites = sprites.Reverse().ToArray();
        string clipPath = string.Format("Assets/{0}/{1}", f2d.clipPath, f2d.resPath);
        if (!Directory.Exists(clipPath))
        {
            Directory.CreateDirectory(clipPath);
        }

        return  BuildAnimationClip(clipPath, animationName, sprites, f2d.frameRate, f2d.frameNum,
            f2d.isLoop, curveBindingType);
    }


    static AnimationClip BuildAnimationClip(string directoryPath, string animationName, Sprite[] sprites, int frameRata, int frameNum, bool isLoop, System.Type curveBindingType)
    {
        AnimationClip clip = new AnimationClip();

#if !UNITY_5
        AnimationUtility.SetAnimationType(clip, ModelImporterAnimationType.Generic);
#endif

        EditorCurveBinding curveBinding = new EditorCurveBinding();
        curveBinding.type = curveBindingType;
        curveBinding.path = "";
        curveBinding.propertyName = "m_Sprite";
        ObjectReferenceKeyframe[] keyFrames = new ObjectReferenceKeyframe[sprites.Length];

        //动画帧率，这里统一用24帧
        clip.frameRate = frameRata;

        //这里的keyFrames[i].time是float类型，而float的精度会影响到sprite的切换，解决方法就是使用double类型计算time。。。。
        //动画长度是按秒为单位，1/10就表示1秒切10张图片，根据项目的情况可以自己调节
        double frameTime = 1d / frameRata;
        for (int i = 0; i < sprites.Length; i++)
        {
            Sprite sprite = sprites[i];
            keyFrames[i] = new ObjectReferenceKeyframe();
            keyFrames[i].time = (float)(frameTime * i);
            keyFrames[i].value = sprite;
        }

        //有些动画我希望天生它就动画循环
        if (isLoop)
        {
            SerializedObject serializedClip = new SerializedObject(clip);
            AnimationClipSettings clipSettings = new AnimationClipSettings(serializedClip.FindProperty("m_AnimationClipSettings"));
            clipSettings.loopTime = true;
            serializedClip.ApplyModifiedProperties();
        }

        AnimationUtility.SetObjectReferenceCurve(clip, curveBinding, keyFrames);
        string path = String.Format("{0}/{1}.anim", directoryPath, animationName);
        AssetDatabase.CreateAsset(clip, path);
        AssetDatabase.SaveAssets();

        return clip;
    }

    /// <summary>
    /// 创建动画覆盖控制器
    /// </summary>
    static AnimatorOverrideController BuildAnimationOverrideController(Dictionary<string, AnimationClip> clips,string parentDir, string directoryName, string name)
    {
        RuntimeAnimatorController animatorController = AssetDatabase.LoadAssetAtPath<AnimatorController>(string.Format("Assets/{0}/AnimatorCom/Fruit2D_Base.controller", parentDir));

        var animatorOverrideController = new AnimatorOverrideController(animatorController);
        List<KeyValuePair<AnimationClip, AnimationClip>> overrides = new List<KeyValuePair<AnimationClip, AnimationClip>>();
        animatorOverrideController.GetOverrides(overrides);

        for (int i = 0; i < overrides.Count; i++)
        {
            var c = overrides[i].Key;
            if (clips.ContainsKey(c.name.ToLower()))
            {
                var k = overrides[i];
                KeyValuePair<AnimationClip, AnimationClip> keyValuePair = new KeyValuePair<AnimationClip, AnimationClip>(k.Key, clips[c.name.ToLower()]);
                overrides[i] = keyValuePair;
            }
        }

        animatorOverrideController.ApplyOverrides(overrides);

        AssetDatabase.CreateAsset(animatorOverrideController, string.Format("{0}/{1}.overrideController", directoryName, name));
        AssetDatabase.SaveAssets();

        return animatorOverrideController;
    }

    static void BuildPrefab(string directoryName, string goName, AnimatorOverrideController animatorCountorller, Sprite defaultSprite, System.Type curveBindingType)
    {
        //生成Prefab 添加一张预览用的Sprite
        GameObject go = new GameObject(goName);

        if (curveBindingType == typeof(SpriteRenderer))
        {
            SpriteRenderer spriteRender = go.AddComponent<SpriteRenderer>();
            spriteRender.sprite = defaultSprite;
            spriteRender.sortingLayerName = "Effect";
        }

        Animator animator = go.AddComponent<Animator>();
        animator.runtimeAnimatorController = animatorCountorller;
        animator.cullingMode = AnimatorCullingMode.CullCompletely;

        PrefabUtility.CreatePrefab(string.Format("{0}/{1}.prefab", directoryName, goName), go);
        GameObject.DestroyImmediate(go);
    }
}
